/*
 * compiler.h
 *
 *  Created on: Sep 13, 2013
 *      Author: Ken Arok
 */

#ifndef COMPILER_H_
#define COMPILER_H_

#if defined(__GNUC__)
#include <avr/io.h>
#else
#error Unsupported compiler
#endif

#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>

#include <parts.h>

/**
 * \def UNUSED
 * \brief Marking \a v as a unused parameter or value.
 */
#define UNUSED(v)          (void)(v)

/**
 * \def unused
 * \brief Marking \a v as a unused parameter or value.
 */
#define unused(v)          do { (void)(v); } while(0)

/**
 * \def barrier
 * \brief Memory barrier
 */
#ifdef __GNUC__
#  define barrier()        asm volatile("" ::: "memory")
#else
#  define barrier()        asm ("")
#endif

/*
 * AVR arch does not care about alignment anyway.
 */
#define COMPILER_PACK_RESET(alignment)
#define COMPILER_PACK_SET(alignment)

//_____ M A C R O S ________________________________________________________


/**
 * \def __always_inline
 * \brief The function should always be inlined.
 *
 * This annotation instructs the compiler to ignore its inlining
 * heuristics and inline the function no matter how big it thinks it
 * becomes.
 */
#if (defined __GNUC__)
#define __always_inline   inline __attribute__((__always_inline__))
#endif

/**
 * \def __always_optimize
 * \brief The function should always be optimized.
 *
 * This annotation instructs the compiler to ignore global optimization
 * settings and always compile the function with a high level of
 * optimization.
 */
#if (defined __GNUC__)
#define __always_optimize   __attribute__((optimize(3)))
#endif


/*! \brief This macro is used to test fatal errors.
 *
 * The macro tests if the expression is false. If it is, a fatal error is
 * detected and the application hangs up. If TEST_SUITE_DEFINE_ASSERT_MACRO
 * is defined, a unit test version of the macro is used, to allow execution
 * of further tests after a false expression.
 *
 * \param expr  Expression to evaluate and supposed to be nonzero.
 */
#if defined(_ASSERT_ENABLE_)
#if defined(TEST_SUITE_DEFINE_ASSERT_MACRO)
	// Assert() is defined in unit_test/suite.h
#include "unit_test/suite.h"
#else
#define Assert(expr) \
	{\
		if (!(expr)) while (true);\
	}
#endif
#else
#define Assert(expr) ((void) 0)
#endif


/*! \name MCU Endianism Handling
 */
//! @{
#define MSB(u16)             (((uint8_t* )&u16)[1])
#define LSB(u16)             (((uint8_t* )&u16)[0])
//! @}

#include "interrupt.h"
#include "progmem.h"

#if (defined __GNUC__)
#define SHORTENUM                           __attribute__ ((packed))
#endif

#if (defined __GNUC__)
#define FUNC_PTR                            void *
#endif


#if (defined __GNUC__)
#define FLASH_DECLARE(x)                  const x __attribute__((__progmem__))
#endif

#if (defined __GNUC__)
#define FLASH_EXTERN(x) extern const x
#endif


/*Defines the Flash Storage for the request and response of MAC*/
#define CMD_ID_OCTET    (0)

/* Converting of values from CPU endian to little endian. */
#define CPU_ENDIAN_TO_LE16(x)   (x)
#define CPU_ENDIAN_TO_LE32(x)   (x)
#define CPU_ENDIAN_TO_LE64(x)   (x)

/* Converting of values from little endian to CPU endian. */
#define LE16_TO_CPU_ENDIAN(x)   (x)
#define LE32_TO_CPU_ENDIAN(x)   (x)
#define LE64_TO_CPU_ENDIAN(x)   (x)

/* Converting of constants from little endian to CPU endian. */
#define CLE16_TO_CPU_ENDIAN(x)  (x)
#define CLE32_TO_CPU_ENDIAN(x)  (x)
#define CLE64_TO_CPU_ENDIAN(x)  (x)

/* Converting of constants from CPU endian to little endian. */
#define CCPU_ENDIAN_TO_LE16(x)  (x)
#define CCPU_ENDIAN_TO_LE32(x)  (x)
#define CCPU_ENDIAN_TO_LE64(x)  (x)

#if (defined __GNUC__)
#define ADDR_COPY_DST_SRC_16(dst, src)  memcpy((&(dst)), (&(src)), sizeof(uint16_t))
#define ADDR_COPY_DST_SRC_64(dst, src)  memcpy((&(dst)), (&(src)), sizeof(uint64_t))

/* Converts a 2 Byte array into a 16-Bit value */
#define convert_byte_array_to_16_bit(data) \
    (*(uint16_t *)(data))

/* Converts a 4 Byte array into a 32-Bit value */
#define convert_byte_array_to_32_bit(data) \
    (*(uint32_t *)(data))

/* Converts a 8 Byte array into a 64-Bit value */
#define convert_byte_array_to_64_bit(data) \
    (*(uint64_t *)(data))

/* Converts a 16-Bit value into a 2 Byte array */
#define convert_16_bit_to_byte_array(value, data) \
    ((*(uint16_t *)(data)) = (uint16_t)(value))

/* Converts spec 16-Bit value into a 2 Byte array */
#define convert_spec_16_bit_to_byte_array(value, data) \
    ((*(uint16_t *)(data)) = (uint16_t)(value))

/* Converts spec 16-Bit value into a 2 Byte array */
#define convert_16_bit_to_byte_address(value, data) \
    ((*(uint16_t *)(data)) = (uint16_t)(value))

/* Converts a 32-Bit value into a 4 Byte array */
#define convert_32_bit_to_byte_array(value, data) \
    ((*(uint32_t *)(data)) = (uint32_t)(value))

/* Converts a 64-Bit value into  a 8 Byte array */
/* Here memcpy requires much less footprint */
#define convert_64_bit_to_byte_array(value, data) \
    memcpy((data), (&(value)), sizeof(uint64_t))
#endif

#define MEMCPY_ENDIAN memcpy
#define PGM_READ_BLOCK(dst, src, len) memcpy_P((dst), (src), (len))

#if (defined __GNUC__)
#define PGM_READ_BYTE(x) pgm_read_byte(x)
#define PGM_READ_WORD(x) pgm_read_word(x)
#endif


#if (defined __GNUC__)
#define nop() do { __asm__ __volatile__ ("nop"); } while (0)
#endif

#if (defined __GNUC__)
#define FORCE_INLINE(type, name, ...) \
static inline type name(__VA_ARGS__) __attribute__((always_inline)); \
static inline type name(__VA_ARGS__)
#endif

#endif /* COMPILER_H_ */
